#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>

#include "resource.h"

#include "../serial.h"
#include "../config.h"
#include "../obd_pid.h"
#include "../util.h"

#define START_MSG "Enter COM port number and click Set Live Mode to begin."
#define DELAY_MS 15
#define IDT_TIMER1 1

struct disp_info {
	int pid[NUM_LOG_SLOTS];
	int num_pids;
};

BOOL CALLBACK callback(HWND, UINT, WPARAM, LPARAM); 
void clear_display(HWND);
int update_display(HWND, struct serial_dev *, struct disp_info *);
void status_msg(HWND, char *);
int set_livemode(HWND, struct serial_dev *);

int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine,
    int iCmdShow) 
{
	MSG msg;
	HWND hDlg;

	OleInitialize(NULL);

	hDlg = CreateDialogParam(hInstance,
	    MAKEINTRESOURCE(IDD_DIALOG1), NULL, callback, (LPARAM)
	    hInstance);
	if (hDlg <= 0) {
		MessageBox(NULL, "Error creating dialog box.", "Error",
		    MB_OK | MB_ICONERROR);
		exit(1);
	}
	ShowWindow(hDlg, SW_SHOWNORMAL);
	UpdateWindow(hDlg);

	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	OleUninitialize();

	return msg.wParam;
}

BOOL CALLBACK
callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	char buf[64];
	int com_num;
	static struct serial_dev sdev;
	static struct disp_info di;
	int i;

	switch(uMsg) {
	case WM_INITDIALOG:
		// lParam is hInstance
		{
			HICON hicon;
			hicon = LoadIcon((HINSTANCE) lParam,
			    MAKEINTRESOURCE(IDI_ICON1)); 
			if (hicon == NULL) {
				MessageBox(hwnd, "Could not set icon.",
				    "Error", MB_OK | MB_ICONERROR);
				DestroyWindow(hwnd);
			}
			SendMessage(hwnd, WM_SETICON, ICON_SMALL,
			    (LPARAM) hicon);
			SendMessage(hwnd, WM_SETICON, ICON_BIG,
			    (LPARAM) hicon);
		}
		status_msg(hwnd, START_MSG);
		for (i = 0; i < NUM_LOG_SLOTS; ++i)
			di.pid[i] = -1;
		di.num_pids = 0;
		break;

	case WM_COMMAND:
		if (HIWORD(wParam) == BN_CLICKED) {
			switch(LOWORD(wParam)) {
			case IDC_SETLIVE:
				SendDlgItemMessage(hwnd, IDC_EDIT_COM,
				    WM_GETTEXT, sizeof(buf), (LPARAM)
				    buf);
				com_num = atoi(buf);
				snprintf(buf, sizeof(buf), "Connecting"
				    " to COM%d...", com_num);
				status_msg(hwnd, buf);
				snprintf(buf, sizeof(buf), "\\\\.\\COM%d",
				    com_num);
				if (init_serial(&sdev, buf) == -1) {
					status_msg(hwnd, "Error "
					    "initializing "
					    "serial port.");
					Sleep(1000);
					status_msg(hwnd, START_MSG);
					return TRUE;
				}

				if (set_livemode(hwnd, &sdev) == -1) {
					Sleep(1000);
					status_msg(hwnd, START_MSG);
					return TRUE;
				}

				SetTimer(hwnd, IDT_TIMER1, DELAY_MS, 0);
				break;
			}
		}
		break;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	
	case WM_TIMER:
		switch (wParam) {
			case IDT_TIMER1:
				update_display(hwnd, &sdev, &di);
				break;
		}
		break;
		
	default:
		return FALSE;
	}

	return TRUE;
}

int
update_display(HWND hwnd, struct serial_dev *sdev, struct disp_info *di)
{
	static char rxbuf[256];
	static int buf_used = 0;
	char buf[64];
	int r;
	char *p, *q;
	int pid, val;

	printf("buf used: %d\n", buf_used);

	if (buf_used < sizeof(rxbuf) - 1) {
		r = read_ser(sdev, &rxbuf[buf_used], sizeof(rxbuf) - buf_used - 1);
		buf_used += r;
		rxbuf[buf_used] = '\0';
	}

	for (;;) {
		p = strchr(rxbuf, '\n');
		if (p == NULL)
			return 0;

		buf_used -= (p - rxbuf + 1);

		*p = '\0';

		q = strchr(rxbuf, '=');
		if (q != NULL) {
			int i;
			int found = 0;
			int desc_edit_id;
			int val_edit_id;
			int radio_id;

			pid = atoi(rxbuf);
			val = atoi(q + 1);
			printf("val is: %d, pid %d\n", val, pid);
			for (i = 0; i < di->num_pids; ++i) {
				if (di->pid[i] == pid) {
					found = 1;
					break;
				}
			}

			if (!found) {
				di->pid[i] = pid;
				++di->num_pids;
			}

			switch (i) {
			case 0:
				desc_edit_id = IDC_EDIT_DESC1;
				val_edit_id = IDC_EDIT_VAL1;
				radio_id = IDC_RADIO1;
				break;
			case 1:
				desc_edit_id = IDC_EDIT_DESC2;
				val_edit_id = IDC_EDIT_VAL2;
				radio_id = IDC_RADIO2;
				break;
			case 2:
				desc_edit_id = IDC_EDIT_DESC3;
				val_edit_id = IDC_EDIT_VAL3;
				radio_id = IDC_RADIO3;
				break;
			case 3:
				desc_edit_id = IDC_EDIT_DESC4;
				val_edit_id = IDC_EDIT_VAL4;
				radio_id = IDC_RADIO4;
				break;
			case 4:
				desc_edit_id = IDC_EDIT_DESC5;
				val_edit_id = IDC_EDIT_VAL5;
				radio_id = IDC_RADIO5;
				break;
			case 5:
				desc_edit_id = IDC_EDIT_DESC6;
				val_edit_id = IDC_EDIT_VAL6;
				radio_id = IDC_RADIO6;
				break;
			case 6:
				desc_edit_id = IDC_EDIT_DESC7;
				val_edit_id = IDC_EDIT_VAL7;
				radio_id = IDC_RADIO7;
				break;
			case 7:
				desc_edit_id = IDC_EDIT_DESC8;
				val_edit_id = IDC_EDIT_VAL8;
				radio_id = IDC_RADIO8;
				break;
			case 8:
				desc_edit_id = IDC_EDIT_DESC9;
				val_edit_id = IDC_EDIT_VAL9;
				radio_id = IDC_RADIO9;
				break;
			}

			snprintf(buf, sizeof(buf), "%s "
			    "(0x%02x)", pid_name[di->pid[i]],
			    di->pid[i]);
			SendDlgItemMessage(hwnd, desc_edit_id,
			    WM_SETTEXT, 0, (LPARAM) buf);
			printf("pid: %d, val: %d\n", di->pid[i], val);
			snprintf(buf, sizeof(buf), "%.2f %s",
			    conv_pid_data(di->pid[i], val),
			    pid_units(di->pid[i]));
			SendDlgItemMessage(hwnd, val_edit_id,
			    WM_SETTEXT, 0, (LPARAM) buf);

			r = SendDlgItemMessage(hwnd, radio_id, BM_GETSTATE, 0, 0);
			if (r & BST_CHECKED)
				r = BST_UNCHECKED;
			else
				r = BST_CHECKED;

			SendDlgItemMessage(hwnd, radio_id, BM_SETCHECK, r, 0);
		}

		memcpy(rxbuf, p + 1, buf_used);
	}
}

void
status_msg(HWND hwnd, char *msg)
{
	SendDlgItemMessage(hwnd, IDC_STATUS, WM_SETTEXT, 0, (LPARAM)
	    msg);
}

int
set_livemode(HWND hwnd, struct serial_dev *sdev)
{
	char buf[128];
	int r;

	strcpy(buf, "!!!\recho off\r");
	r = write_ser(sdev, buf, strlen(buf));
	if (r != strlen(buf)) {
		status_msg(hwnd, "Error writing to serial.");
		return -1;
	}

	ser_flush(sdev);
	
	strcpy(buf, "set_livemode\r");
	r = write_ser(sdev, buf, strlen(buf));
	if (r != strlen(buf)) {
		status_msg(hwnd, "Error writing to serial.");
		return -1;
	}

	r = read_ser(sdev, buf, sizeof(buf));
	buf[r] = '\0';
	if (r == 0) {
		status_msg(hwnd, "Error reading from serial.");
		return -1;
	}

	if (!strncmp(buf, "OK", 2)) {
		status_msg(hwnd, "Set live mode, waiting for data...");
	} else {
		status_msg(hwnd, "Could not set live mode.");
		return -1;
	}

	return 0;
}
